package com.yang.limit.service;

import com.yang.limit.annotation.RequestLimit;
import com.yang.limit.constant.RequestLimitType;
import com.yang.limit.dto.RequestLimitDTO;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

public interface RequestLimitService {

    /**
     * 获取限流的类型
     * @return
     */
    RequestLimitType getLimitType();

    /**
     * 检测当前请求是否会被限流
     */
    boolean checkIsRequestLimit(RequestLimitDTO dto);

    /**
     * 获取工程内所有标注了限流注解的相关信息
     * @param resourcePatternResolver
     * @param limitType  限流策略
     * @param scanPackage 扫描包路径
     * @return
     */
    default List<RequestLimitDTO> getTokenLimitList(ResourcePatternResolver resourcePatternResolver, RequestLimitType limitType, String scanPackage) {
        try {
            List<RequestLimitDTO> list = new ArrayList<>();
            Resource[] resources = resourcePatternResolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + scanPackage + "/**/*.class");
            MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
            for (Resource resource : resources) {
                MetadataReader reader = metaReader.getMetadataReader(resource);
                AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                Set<MethodMetadata> annotatedMethods = annotationMetadata.getAnnotatedMethods(RequestLimit.class.getCanonicalName());
                annotatedMethods.forEach(methodMetadata -> {
                    RequestLimit limiter = methodMetadata.getAnnotations().get(RequestLimit.class).synthesize();
                    if (!limitType.equals(limiter.type())) {
                        return;
                    }
                    RequestLimitDTO dto = new RequestLimitDTO();
                    dto.setKey(methodMetadata.getMethodName());
                    dto.setLimiter(limiter);
                    list.add(dto);
                });
            }
            return list;
        } catch (IOException e) {
            return Collections.emptyList();
        }
    }

}
